package com.wonder.app.service.impl;

import cn.hutool.core.date.DateTime;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vtuzx.core.bean.dto.VtuzxMap;
import com.vtuzx.core.utils.VtuzxUtil;
import com.wonder.admin.domain.Parameter;
import com.wonder.admin.mapper.ParameterDao;
import com.wonder.admin.service.IVtuzxKingdeeService;
import com.wonder.admin.service.ParameterService;
import com.wonder.admin.service.impl.VtuzxKingdeeServiceImpl;
import com.wonder.app.domain.*;
import com.wonder.app.mapper.*;
import com.wonder.app.request.SfcOperpRequest;
import com.wonder.app.request.SfcOptrptRequest;
import com.wonder.app.service.*;
import com.wonder.bean.kingdee.VtuzxKingdeeCloudClient;
import com.wonder.bean.kingdee.xqc.VtuzxArrayList;
import com.wonder.common.common.AjaxResult;
import com.wonder.common.utils.DateUtils;
import com.wonder.common.utils.MapToEntity;
import com.wonder.common.utils.MessageUtils;
import com.wonder.common.utils.StringUtils;
import com.wonder.constant.BillType;
import com.wonder.constant.IXqcConst;
import jnr.constants.platform.PRIO;
import org.apache.commons.lang3.Conversion;
import org.bson.Document;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.web3j.abi.datatypes.primitive.Int;

import javax.sound.sampled.DataLine;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SfcOptrptServiceImpl extends ServiceImpl<SfcOptrptDao,SfcOptrpt> implements SfcOptrptService{
    /** 日志 */
    private static final Logger _logger = LoggerFactory.getLogger(SfcOptrptServiceImpl.class);
    @Autowired
    private SfcOptrptService sfcOptrptService;
    @Autowired
    private  SfcOptrptDao sfcOptrptDao;
    @Autowired
    private SfcOptrptDetailDao sfcOptrptDetailDao;
    @Autowired
    private SfcOptrptDetailService sfcOptrptDetailService;

    @Autowired
    private MoldMaterialDao moldMaterialDao;

    @Autowired
    private TrayService trayService;

    @Autowired
    private PrdIronTrayService prdIronTrayService;

    @Autowired
    private PrdIronTrayDao prdIronTrayDao;

    @Autowired
    private ParameterService parameterService;

    @Autowired
    private ParameterDao parameterDao;

    @Autowired
    private PrdFeedService prdFeedService;

    @Autowired
    private  IVtuzxKingdeeService kingdeeService;

    @Autowired
    private SyncKingdeeOrderService syncKingdeeOrderService;
    @Autowired
    private SfcProplanService sfcProplanService;
    @Autowired
    private SfcProplanDao sfcProplanDao;
    @Autowired
    private SfcproplanDetailDao sfcproplanDetailDao;
    @Autowired
    private SfcproplanDetailService sfcproplanDetailService;

    @Autowired
    private PrdReturnService prdReturnService;

    @Autowired
    private PrdInstockService prdInstockService;

    @Autowired
    private PrdInstockDetailDao prdInstockDetailDao;

    @Autowired
    private PrdInstockDetailService prdInstockDetailService;

    @Autowired
    private SfcOptrptResService sfcOptrptResService;

    @Autowired
    private SfcOptrptResDao sfcOptrptResDao;

    @Autowired
    private PrdInstockDao prdInstockDao;

    @Autowired
    private PrdPickService prdPickService;

    @Autowired
    private SfcOptrptLogService sfcOptrptLogService;

    @Override
    public IPage<SfcOptrpt> selectList(SfcOptrptRequest Request, Page<SfcOptrpt> page) {
//        LambdaQueryWrapper<SfcOptrpt> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//        if (Request.getBillNo() != null){
//            lambdaQueryWrapper.like(SfcOptrpt::getBillNo,Request.getBillNo());
//        }
//        if(Request.getPrdBillNo() != null){
//            lambdaQueryWrapper.like(SfcOptrpt::getPrdBillNo,Request.getPrdBillNo());
//        }
//        if (Request.getPrtUser() != null){
//            lambdaQueryWrapper.like(SfcOptrpt::getPrtUser,Request.getPrtUser());
//        }
//        if(Request.getWorkShopName() != null){
//            lambdaQueryWrapper.like(SfcOptrpt::getWorkShopName,Request.getWorkShopName());
//        }
//        if (Request.getDate() != null){
//            Date[] orderDate = Request.getDate();
//            if(orderDate.length>0)
//                lambdaQueryWrapper.ge(SfcOptrpt::getDate, orderDate[0]);
//            if(orderDate.length==2)
//                lambdaQueryWrapper.le(SfcOptrpt::getDate,orderDate[1]);
//
//        }
//        lambdaQueryWrapper.orderByDesc(SfcOptrpt::getDate);
//        Page<SfcOptrpt> selectPage = this.baseMapper.selectPage(page, lambdaQueryWrapper);
//        return selectPage;
        IPage<SfcOptrpt> result = sfcOptrptDao.getOptrptWithDetail(page, Request);

        return result;

    }

    /**
     * 工序汇报明细详情
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> getSfcOptrptDetail(Map<String, Object> map) {
        String billNo = map.get("billNo").toString();
        SfcOptrpt sfcOptrptform = sfcOptrptDao.selectOne(new LambdaQueryWrapper<SfcOptrpt>()
                .eq(SfcOptrpt::getBillNo,billNo));
        List<SfcOptrptDetail> saleoptrptdetailList = sfcOptrptDetailDao.selectList(new LambdaQueryWrapper<SfcOptrptDetail>()
                .eq(SfcOptrptDetail::getBillNo,billNo));
        Map<String,Object> data = new HashMap<>();
        data.put("data",sfcOptrptform);
        data.put("dataList",saleoptrptdetailList);
        return data;
    }
    /**
     * 工序汇报明细手机端详情
     * @param map
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> getOptrptisDetail(Map<String, Object> map) {
        String billNo = map.get("billNo").toString();
        String moBillNo = map.get("moBillNo").toString();
        String operNo = map.get("operNo").toString();

        // 1. 查询主表列表(只查询需要质检的)
        List<SfcOptrpt> masterList = sfcOptrptDao.selectList(new LambdaQueryWrapper<SfcOptrpt>()
                .eq(SfcOptrpt::getErpBillNo, billNo)
                .eq(SfcOptrpt::getPrdBillNo, moBillNo)
                .eq(SfcOptrpt::getStatus,"save")
                .eq(SfcOptrpt::getOperpNo, operNo).orderByDesc(SfcOptrpt::getDate));
        if (VtuzxUtil.isEmpty(masterList)){
            throw new RuntimeException("当前工序不存在需要质检的汇报单");
        }

        // 2. 创建结果列表（每个元素包含一个主表及其对应的明细）
        List<Map<String, Object>> combinedList = new ArrayList<>();

        // 3. 处理每个主表
        for (SfcOptrpt master : masterList) {
            // 查询该主表对应的明细
            List<SfcOptrptDetail> details = sfcOptrptDetailDao.selectList(
                    new LambdaQueryWrapper<SfcOptrptDetail>()
                            .eq(SfcOptrptDetail::getBillNo, master.getBillNo()) // 关联主表ID
                            .eq(SfcOptrptDetail::getOperpNo, operNo)
            );

            // 将主表和明细"捏在一起"
            Map<String, Object> combinedData = new HashMap<>();
            combinedData.put("master", master);     // 主表数据
            combinedData.put("details", details);   // 对应的明细列表
            combinedList.add(combinedData);
        }

        // 4. 返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("data", combinedList);
        return result;
    }
    /**
     * 创建工序汇报单按序汇报
     * @param map
     * @return
     */
    @Override
    public boolean createReport(Map<String,Object> map,String username,Long user_id) throws Exception{
        Map<String,Object> operpData = VtuzxUtil.getObject(map,"operpData");
        Map<String,Object> header = VtuzxUtil.getObject(map,"header");
        String departName = VtuzxUtil.getString(operpData,"departName");//车间信息
        String trayNo = VtuzxUtil.getObject(map,"trayNo");//新生成的托盘号
        String flot = VtuzxUtil.getObject(map,"flot");//批号
//        String ironNo = VtuzxUtil.getObject(map,"ironNo");//铁托号
        Double failCount = 0.0;//废料数量合计
        String specification = VtuzxUtil.getString(header, "specification");//件号
        //如果是砂芯车间,同一个件号汇报时间不能超过10分钟(从参数表中获取)
        if (departName.contains("砂芯")){
            Parameter parameter = parameterDao.selectById(54);
            String value = parameter.getValue();//不允许超过的时间
            //查询当前件号上一次汇报的时间
            SfcOptrptRes sfcOptrptRes = sfcOptrptResDao.selectOne(new LambdaQueryWrapper<SfcOptrptRes>()
                    .eq(SfcOptrptRes::getSpecification, specification)
                    .orderByDesc(SfcOptrptRes::getCreateTime)
                    .last("limit 1"));
            if (!VtuzxUtil.isEmpty(sfcOptrptRes)){
                Date createTime = sfcOptrptRes.getCreateTime();//当前件号上次汇报的时间
                Long aLong = DateUtils.compareDateMin(createTime, new Date());
                if (aLong < Long.parseLong(value)){
                    //捕获异常删除已经生成的托盘信息
                    trayService.remove(new LambdaQueryWrapper<Tray>()
                            .eq(Tray::getFBILLNO, trayNo));
                    throw new RuntimeException(String.format("距离上次汇报时间没有超过%s分钟,不允许再次汇报",value));
                }
            }
        }
        String control = VtuzxUtil.getString(operpData,"control");
        SfcOptrpt sfcOptrpt = new SfcOptrpt();
        String REPORTkBillNo =  IXqcConst.createBillNo(BillType.GXHB);//生成工序汇报单
        String ERPBIllNo = VtuzxUtil.getString(header,"billNo");//工序计划单号
        int Beginandend = VtuzxUtil.getInt(operpData,"beginandend");//首序末序0首序1末序


        sfcOptrpt.setBillNo(REPORTkBillNo);
        sfcOptrpt.setDate(new Date());
        sfcOptrpt.setErpBillNo(ERPBIllNo);
        sfcOptrpt.setWorkShopNo(VtuzxUtil.getString(operpData,"departNo"));
        sfcOptrpt.setWorkShopName(departName);
        sfcOptrpt.setPrtUser(username);
        sfcOptrpt.setBeginandend(Beginandend);
        sfcOptrpt.setPushDown(false);
        sfcOptrpt.setOperpNo(VtuzxUtil.getLong(operpData,"operNo"));
        sfcOptrpt.setPrdBillNo(VtuzxUtil.getString(header,"moBillNo"));//生产订单编号
        sfcOptrpt.setTrayNo(trayNo);
        //汇报+置后检，就是免检，汇报+质检，强制检验
        if (control.contains("质检")){
            sfcOptrpt.setStatus("save");
        } else {
            sfcOptrpt.setStatus("audit");
        }
  
        List<SfcOptrptDetail> sfcOptrptDetailList = new ArrayList<>();//需要添加到汇报单详情的数据

        //构建单据体
        SfcOptrptDetail detail = new SfcOptrptDetail();
        detail.setBillNo(REPORTkBillNo);
        detail.setMaterialNo(VtuzxUtil.getString(header,"materialNo"));
        detail.setMaterialName(VtuzxUtil.getString(header,"materialName"));
        detail.setSpecification(VtuzxUtil.getString(header,"specification"));
        detail.setUnitNo(VtuzxUtil.getString(header,"unitNo"));
        detail.setUnitName(VtuzxUtil.getString(header,"unitName"));
        detail.setDescribtion(VtuzxUtil.getString(operpData,"operName"));
        detail.setQuaQty(VtuzxUtil.getDouble(map,"quaQty"));//合格数量
        detail.setProcessFailQty(VtuzxUtil.getDouble(map,"processFailQty"));//工废数量
        failCount += VtuzxUtil.getDouble(map,"processFailQty");
        detail.setMaterialFailQty(VtuzxUtil.getDouble(map,"materialFailQty"));//料废数量
        failCount += VtuzxUtil.getDouble(map,"materialFailQty");
        detail.setWorkTime(VtuzxUtil.getDouble(map,"workTime"));//工时
        detail.setEndQty(VtuzxUtil.getDouble(map,"endQty"));//完工数量
        detail.setOperator(username);//操作工
        detail.setBeginandend(Beginandend);//首序末序
        detail.setOperpNo(VtuzxUtil.getLong(operpData,"operNo"));
        detail.setInStockTime(DateUtils.strToDate(VtuzxUtil.getString(map,"inStockTime"),"yyyy-MM-ddHH:mm:ss"));
        detail.setTrayNo(trayNo);//托盘编号
        detail.setFlot(flot);//批号

        sfcOptrptDetailList.add(detail);

        //传金蝶生产汇报单
        try {
            syncKingdeeOrderService.syncPrdReport(user_id,sfcOptrpt,sfcOptrptDetailList);
        } catch (Exception e){
            //捕获异常删除已经生成的托盘信息
            trayService.remove(new LambdaQueryWrapper<Tray>()
                    .eq(Tray::getFBILLNO, trayNo));
            throw new RuntimeException(e.getMessage());
        }

        //保存到数据库
        boolean savehead = sfcOptrptService.save(sfcOptrpt);
        boolean saveBatch =  sfcOptrptDetailService.saveBatch(sfcOptrptDetailList);
        //保存成功后添加汇报日志
        SfcOptrptRes sfcOptrptRes = new SfcOptrptRes();
        sfcOptrptRes.setPlanNo(ERPBIllNo);
        sfcOptrptRes.setOperNo(VtuzxUtil.getLong(operpData,"operNo"));
        sfcOptrptRes.setCreateUserName(username);
        sfcOptrptRes.setCreateTime(new Date());
        sfcOptrptRes.setTrayNo(trayNo);
        sfcOptrptRes.setSpecification(specification);
        sfcOptrptRes.setWorkShopName(departName);
        sfcOptrptResService.save(sfcOptrptRes);

        //修改铁托的状态
//        prdIronTrayService.update(
//                null, Wrappers.<PrdIronTray>lambdaUpdate()
//                        .eq(PrdIronTray::getBillNo,ironNo)
//                        .set(PrdIronTray::getStatus,true));

        //如果是末序汇报，不管需不需要质检，直接生成入库单
        if (Beginandend == 1){
            prdInstockService.createPrdInstock(map, username,user_id,REPORTkBillNo,failCount);
        }

        return  savehead & saveBatch;



    }


//    /**
//     * 校验模具是否归还
//     * @param proPlanNo
//     * @return
//     */
//    private Boolean checkModStatus(String proPlanNo) throws Exception{
//        SfcProplan sfcProplan = sfcProplanDao.selectOne(new LambdaQueryWrapper<SfcProplan>()
//                .eq(SfcProplan::getBillNo, proPlanNo));
//        Boolean moldStatus = sfcProplan.getMoldStatus();
//        if (!moldStatus){
//            throw new RuntimeException("当前工单没有领用模具,无法汇报最后一道工序");
//        }
//        String moldId = sfcProplan.getMoldId();//当前工单领用的模具id
//        List<String> moldIdList = Arrays.asList(moldId.split(","));
//        //查询模具表，判断模具的状态是否都在库，在库的话说明都归还了
//        List<MoldMaterial> moldMaterialList = moldMaterialDao.selectBatchIds(moldIdList);
//        //有一个状态不是0的就会返回true，说明有模具未归还
//        boolean notAllZero = moldMaterialList.stream()
//                .anyMatch(m -> m.getStatus() != 0);
//    }
    /**
     * 审核工序汇报单
     * @param map
     * @param userName
     * @return
     * @throws Exception
     */
    @Override
    public boolean auditReport(Map<String, Object> map, String userName,Long user_id) throws Exception {
        //审核需要修改单据头的状态，修改单据头的不合格数量，如果是末序的话还要生成入库单
        Map<String,Object> master = VtuzxUtil.getObject(map,"master");//汇报单据头
        List<Map<String,Object>> details = VtuzxUtil.getObject(map,"details");//汇报单据体
        String billNo = VtuzxUtil.getString(master,"billNo");//汇报单号
        String operpNo = VtuzxUtil.getString(master,"operpNo");//工序号
        double unQuaqty = VtuzxUtil.getDouble(details.get(0),"unQuaqty");//不合格数量
        int Beginandend = VtuzxUtil.getInt(details.get(0),"beginandend");//首序末序0首序1末序 = VtuzxUtil.getDouble(details.get(0),"unQuaqty");//不合格数量
        //修改单据状态
        SfcOptrpt sfcOptrpt = sfcOptrptDao.selectOne(new LambdaQueryWrapper<SfcOptrpt>()
            .eq(SfcOptrpt::getBillNo, billNo));
        sfcOptrpt.setStatus("audit");


        //修改不合格数量
        SfcOptrptDetail sfcOptrptDetail = sfcOptrptDetailDao.selectOne(new LambdaQueryWrapper<SfcOptrptDetail>()
                .eq(SfcOptrptDetail::getBillNo, billNo));
        sfcOptrptDetail.setUnQuaqty(unQuaqty);


        //如果是末序汇报并生成入库单
        if (Beginandend == 1){
            prdInstockService.createPrdInstockByCheck(master, details.get(0),userName,user_id);
        }
        boolean res = sfcOptrptService.updateById(sfcOptrpt);
        boolean detailRes = sfcOptrptDetailService.updateById(sfcOptrptDetail);
        return res && detailRes;

    }

    /**
     * 根据工序计划单号获取工序汇报
     * @param map
     * @param userName
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> getReportByOperNo(Map<String, Object> map, String userName) throws Exception {
        List<String> billNoList = VtuzxUtil.getObject(map,"billNoList");
        List<SfcProplan> sfcProplans = sfcProplanDao.selectList(new LambdaQueryWrapper<SfcProplan>()
                .in(SfcProplan::getBillNo, billNoList));
        Map<String,Object> planMap = new HashMap<>();//key:工单号，val:汇报单信息
        for (SfcProplan sfcProplan : sfcProplans){
            String billNo = sfcProplan.getBillNo();
            planMap.put(billNo,sfcProplan);
        }
        //TODO 需要判断车间是否都是包装车间的

        List<SfcOptrpt> dataList = new ArrayList<>();//最后需要返回的数据
        //根据工单号判断有没有汇报信息，没有的创建新的，有的话获取
        for (String billNo : billNoList){
            SfcOptrpt sfcOptrpt = sfcOptrptDao.selectOne(new LambdaQueryWrapper<SfcOptrpt>()
                    .eq(SfcOptrpt::getErpBillNo, billNo));
            if (VtuzxUtil.isEmpty(sfcOptrpt)){//如果为空
                SfcOptrpt newSfcOptrpt = new SfcOptrpt();
                String optrptNo = IXqcConst.createBillNo(BillType.SCHB);
                SfcProplan sfcProplan = VtuzxUtil.getObject(planMap,billNo);//获取工序计划信息
                newSfcOptrpt.setBillNo(optrptNo);
                newSfcOptrpt.setPrdBillNo(sfcProplan.getMoBillNo());
                newSfcOptrpt.setDate(new Date());
                newSfcOptrpt.setWorkShopNo("BZ");
                newSfcOptrpt.setWorkShopName("包装车间");
                newSfcOptrpt.setErpBillNo(billNo);//工序计划单号
                newSfcOptrpt.setBeginandend(1);//包装就一道序
                newSfcOptrpt.setStatus("save");//保存状态
                newSfcOptrpt.setQuaQty(0.0);//合格数量
                newSfcOptrpt.setFailQty(0.0);//报废数量
                newSfcOptrpt.setOutFailQty(0.0);//外协不良数量
                newSfcOptrpt.setReturnQty(0.0);//返工数量
                newSfcOptrpt.setEndQty(0.0);//完工数量
                newSfcOptrpt.setPrtUser(userName);
                newSfcOptrpt.setSpecification(sfcProplan.getSpecification());
                //数据库保存数据
                boolean save = sfcOptrptService.save(newSfcOptrpt);
                dataList.add(newSfcOptrpt);
            } else {
                dataList.add(sfcOptrpt);
            }
        }
        Map<String,Object> data = new HashMap<>();
        data.put("dataList",dataList);
        return data;
    }

    /**
     * 保存包装间的汇报信息
     * @param map
     * @param userName
     * @return
     * @throws Exception
     */
    @Override
    public Boolean createReportPackage(Map<String, Object> map, String userName) throws Exception {
        List<Map<String,Object>> dataList = VtuzxUtil.getObject(map,"dataList");
        List<SfcOptrpt> sfcOptrptList = new ArrayList<>();
        for (Map<String,Object> data : dataList){
            //添加校验，如果单据已经审核不允许修改
            SfcOptrpt sfcOptrpt = MapToEntity.mapToBean(data,SfcOptrpt.class);
            String status = sfcOptrpt.getStatus();
            if ("audit".equals(status)){
                throw new RuntimeException(String.format("工单:%s,已经审核，不允许重复保存",sfcOptrpt.getErpBillNo()));
            }
            sfcOptrptList.add(sfcOptrpt);
        }
        boolean b = sfcOptrptService.saveOrUpdateBatch(sfcOptrptList);
        return b;
    }

    /**
     * 包装间汇报检验列表
     * @param Request
     * @param page
     * @return
     */
    @Override
    public IPage<SfcOptrpt> packageList(SfcOptrptRequest Request, Page<SfcOptrpt> page) {
        LambdaQueryWrapper<SfcOptrpt> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //车间只查询包装的
        lambdaQueryWrapper.like(SfcOptrpt::getWorkShopNo,"BZ");
        if (Request.getBillNo() != null){
            lambdaQueryWrapper.like(SfcOptrpt::getBillNo,Request.getBillNo());
        }
        if(Request.getPrdBillNo() != null){
            lambdaQueryWrapper.like(SfcOptrpt::getPrdBillNo,Request.getPrdBillNo());
        }
        if (Request.getPrtUser() != null){
            lambdaQueryWrapper.like(SfcOptrpt::getPrtUser,Request.getPrtUser());
        }



        if (Request.getDate() != null){
            Date[] orderDate = Request.getDate();
            if(orderDate.length>0)
                lambdaQueryWrapper.ge(SfcOptrpt::getDate, orderDate[0]);
            if(orderDate.length==2)
                lambdaQueryWrapper.le(SfcOptrpt::getDate,orderDate[1]);

        }
        lambdaQueryWrapper.orderByDesc(SfcOptrpt::getDate);
        Page<SfcOptrpt> selectPage = this.baseMapper.selectPage(page, lambdaQueryWrapper);
        return selectPage;
    }

    /**
     * 审核包装间的汇报信息
     * @param map
     * @param userName
     * @return
     * @throws Exception
     */
    @Override
    public Boolean auditReportPackage(Map<String, Object> map, String userName,Long user_id) throws Exception {
//        List<String> billNoList = VtuzxUtil.getObject(map,"billNoList");//需要审核的单据编号(工序号)
//        //合格数量对应生产入库单，仓库=出货成品仓，领料单，仓库=包装材料库
//        //报废数量对应生产退料单,仓库=回材库，生产补料单，仓库=包装材料库
//        //外协不良数量对应生产退料单,仓库=报废品仓库，生产补料单，仓库=包装材料库
//        //返修数量对应生产退料单,仓库=待不良返修，生产补料单，仓库=包装材料库
//        List<SfcProplan> proplanList = sfcProplanDao.selectList(new LambdaQueryWrapper<SfcProplan>()
//                .in(SfcProplan::getBillNo,billNoList));
//        Map<String,Object> planMap = new HashMap<>();//key:工序单号，工序信息
//        for (SfcProplan sfcProplan : proplanList){
//            planMap.put(sfcProplan.getBillNo(),sfcProplan);
//        }
//        List<SfcOptrpt> sfcOptrptList = sfcOptrptDao.selectList(new LambdaQueryWrapper<SfcOptrpt>()
//                .in(SfcOptrpt::getErpBillNo, billNoList));
//        Map<String,Object> reportMap = new HashMap<>();//key:工单号，val:汇报信息
//
//        for (SfcOptrpt sfcOptrpt : sfcOptrptList){
//            reportMap.put(sfcOptrpt.getErpBillNo(),sfcOptrpt);
//            sfcOptrpt.setStatus("audit");
//            //传金蝶汇报单
//            try {
////                syncKingdeeOrderService.syncPrdReportBao(user_id,sfcOptrpt,null);
//            }catch (Exception e){
//                _logger.info(e.getMessage());
//            }
//        }
//        //先生成领料和入库单
////        prdPickService.createPickBillPackage(user_id,userName,billNoList,planMap,reportMap);
//
//
////        prdInstockService.createPrdInstockPackage(user_id,userName,billNoList,planMap,reportMap);
//
//        List<SfcOptrpt> failList = new ArrayList<>();//报废数量需要生成的退料单
//        List<SfcOptrpt> outList = new ArrayList<>();//外协不良数量需要生成的退料单
//        List<SfcOptrpt> returnList = new ArrayList<>();//返修数量需要生成的退料单
//        //生成补料单和退料单
//        for (String operNo : billNoList){
//            SfcOptrpt sfcOptrpt = VtuzxUtil.getObject(reportMap,operNo);//汇报信息
//            Double failQty = sfcOptrpt.getFailQty();//报废数量
//            Double outFailQty = sfcOptrpt.getOutFailQty();//外协不良数量
//            Double returnQty = sfcOptrpt.getReturnQty();//返修数量
//            if (failQty > 0){
//                failList.add(sfcOptrpt);
//            }
//            if (outFailQty > 0){
//                outList.add(sfcOptrpt);
//            }
//            if (returnQty > 0){
//                returnList.add(sfcOptrpt);
//            }
//        }
//        //TODO 传金蝶生产补料单，通过补料单下推退料单
//        if (!VtuzxUtil.isEmpty(failList)){//生成报废数量的退料和领料
//            prdFeedService.createFeedBillPackage(user_id, userName,failList,planMap,"fail");
//            prdReturnService.createReturnBillPackage(userName,failList,planMap,"fail");
//
//        }
//        if (!VtuzxUtil.isEmpty(outList)){//生产外协不良数量的退料和补料
//            prdFeedService.createFeedBillPackage(user_id, userName,outList,planMap,"out");
//            prdReturnService.createReturnBillPackage(userName,outList,planMap,"out");
//
//        }
//        if (!VtuzxUtil.isEmpty(returnList)){//生产返修数量的退料和补料
//            prdFeedService.createFeedBillPackage(user_id, userName,returnList,planMap,"return");
//            prdReturnService.createReturnBillPackage(userName,returnList,planMap,"return");
//
//        }
//        //修改汇报单的单据状态
//        boolean b = sfcOptrptService.saveOrUpdateBatch(sfcOptrptList);
        return null;
    }

    /**
     * 机加车间创建工序汇报单
     * @param map
     * @param userName
     * @param user_id
     * @return
     * @throws Exception
     */
    @Override
    public boolean createReportJijia(Map<String, Object> map, String userName, Long user_id) throws Exception {
        Map<String,Object> operpData = VtuzxUtil.getObject(map,"operpData");//工序计划单单据头
        Map<String,Object> header = VtuzxUtil.getObject(map,"header");//工序计划单明细
        String departName = VtuzxUtil.getString(operpData,"departName");//车间信息
        String operator = VtuzxUtil.getString(map,"operator");//操作工

        Map<String,Object> trayData = VtuzxUtil.getObject(map,"trayData");//托盘信息
        String trayNo = VtuzxUtil.getString(trayData,"fbillno");//托号
        map.put("trayNo",trayNo);
        int operNo = VtuzxUtil.getInt(operpData,"operNo");//序号

        //先根据托号和工序号查询汇报单,判断这个托盘这道序有没有汇报过,获取到之前汇报的数量
        //之前汇报的完工数 + 此次汇报的完工数不能大于托盘元原有的数量
        //之前汇报的完工数 + 此次汇报的完工数等于托盘原有的数量说明当前托盘汇报完毕,修改托盘的数量为汇总的合格数量
        //之前汇报的完工数 + 此次汇报的完工数小于托盘的数量,继续保存到汇报单中
        double processFailQty = VtuzxUtil.getDouble(map, "processFailQty");//工废数量
        double materialFailQty = VtuzxUtil.getDouble(map, "materialFailQty");//工废数量
        double failQty = processFailQty + materialFailQty;//此次汇报的废料数量合计
        //查询之前汇报的完工数量，合格数量
        Map<String, Object> countMap = optrptCount(operNo, trayNo);
        double sumEndQty = VtuzxUtil.getDouble(countMap,"sumEndQty");//之前汇报的完工数量总和


        double endQty = VtuzxUtil.getDouble(map,"endQty");//此次汇报的完工数量
        double fqty = VtuzxUtil.getDouble(trayData,"fqty");//托盘原有数量
        double optCount = VtuzxUtil.getDouble(trayData,"optCount");//托盘可汇报数量
        if(sumEndQty + endQty > fqty){//汇报的完工数 + 此次汇报的完工数不能大于托盘元原有的数量
            throw new RuntimeException("汇报的数量超过托盘数量,不允许汇报");
        }
        //小于或者等于的话都需要保存汇报单信息

        String control = VtuzxUtil.getString(operpData,"control");
        SfcOptrpt sfcOptrpt = new SfcOptrpt();
        String REPORTkBillNo =  IXqcConst.createBillNo(BillType.GXHB);//生成工序汇报单
        String ERPBIllNo = VtuzxUtil.getString(header,"billNo");//工序计划单号
        int Beginandend = VtuzxUtil.getInt(operpData,"beginandend");//首序末序0首序1末序
        sfcOptrpt.setBillNo(REPORTkBillNo);
        sfcOptrpt.setDate(new Date());
        sfcOptrpt.setErpBillNo(ERPBIllNo);
        sfcOptrpt.setWorkShopNo(VtuzxUtil.getString(operpData,"departNo"));
        sfcOptrpt.setWorkShopName(departName);
        sfcOptrpt.setPrtUser(userName);
        sfcOptrpt.setBeginandend(Beginandend);
        sfcOptrpt.setPushDown(false);
        sfcOptrpt.setOperpNo(VtuzxUtil.getLong(operpData,"operNo"));
        sfcOptrpt.setPrdBillNo(VtuzxUtil.getString(header,"moBillNo"));//生产订单编号
        sfcOptrpt.setTrayNo(trayNo);//托号
        //汇报+置后检，就是免检，汇报+质检，强制检验
        if (control.contains("质检")){
            sfcOptrpt.setStatus("save");
        } else {
            sfcOptrpt.setStatus("audit");
        }

        List<SfcOptrptDetail> sfcOptrptDetailList = new ArrayList<>();//需要添加到汇报单详情的数据

        //构建单据体
        SfcOptrptDetail detail = new SfcOptrptDetail();
        detail.setBillNo(REPORTkBillNo);
        detail.setMaterialNo(VtuzxUtil.getString(header,"materialNo"));
        detail.setMaterialName(VtuzxUtil.getString(header,"materialName"));
        detail.setSpecification(VtuzxUtil.getString(header,"specification"));
        detail.setUnitNo(VtuzxUtil.getString(header,"unitNo"));
        detail.setUnitName(VtuzxUtil.getString(header,"unitName"));
        detail.setDescribtion(VtuzxUtil.getString(operpData,"operName"));
        detail.setQuaQty(VtuzxUtil.getDouble(map,"quaQty"));//合格数量
        detail.setProcessFailQty(processFailQty);//工废数量
        detail.setMaterialFailQty(materialFailQty);//料废数量
        detail.setWorkTime(VtuzxUtil.getDouble(map,"workTime"));//工时
        detail.setEndQty(VtuzxUtil.getDouble(map,"endQty"));//完工数量
        detail.setOperator(operator);//操作工
        detail.setBeginandend(Beginandend);//首序末序
        detail.setOperpNo(VtuzxUtil.getLong(operpData,"operNo"));
        detail.setInStockTime(DateUtils.strToDate(VtuzxUtil.getString(map,"inStockTime"),"yyyy-MM-ddHH:mm:ss"));
        detail.setTrayNo(trayNo);

        sfcOptrptDetailList.add(detail);


        //判断之前是否汇报，汇报过的话之前的完工数+此次的完工数 + 之前的废料数=托数 说明当前工序汇报完了
        //没报过的话,此次的完工数 + 之前的料废数 = 托数 说明当前工序汇报完了
        List<SfcOptrpt> sfcOptrpts = sfcOptrptDao.selectList(new LambdaQueryWrapper<SfcOptrpt>()
                .eq(SfcOptrpt::getTrayNo, trayNo)
                .eq(SfcOptrpt::getWorkShopName,"机加车间")
                .eq(SfcOptrpt::getOperpNo, operNo));
        Double failCount = getFailCount(operNo, trayNo);//之前的废料数量总和
        if (!VtuzxUtil.isEmpty(sfcOptrpts)){//当前工序报过
            if (sumEndQty + endQty == fqty){//当前工序汇报完了
                double sumQuaQty = VtuzxUtil.getDouble(countMap,"sumQuaQty");//之前汇报的合格数量总和
                double quaQty = VtuzxUtil.getDouble(map,"quaQty");//此次汇报的合格数量
                double count = sumQuaQty + quaQty;

                //如果是末序汇报限制只能汇报一次
                if (Beginandend == 1){
                    throw new RuntimeException("当前托盘已经汇报过末序,无法再次汇报末序");
                }
                try {
                    //绑定托盘已经汇报过的工序号，并且添加
                    trayService.bandingProcess(trayData,operpData,count);
                } catch (Exception e){
                    throw new RuntimeException(e.getMessage());
                }

            }
        } else {//如果当前序没有汇报过此次的完工数 + 之前的料废数 = 托数 说明当前工序汇报完了
            //判断是否是末序(末序需要校验只汇报一次)
            if (Beginandend == 1){//这次的完工数量 + 之前的废料数量 = 托盘数说明汇报完了
                if (failCount + endQty == fqty){
                    double sumQuaQty = VtuzxUtil.getDouble(countMap,"sumQuaQty");//之前汇报的合格数量总和
                    double quaQty = VtuzxUtil.getDouble(map,"quaQty");//此次汇报的合格数量
                    double count = sumQuaQty + quaQty;
                    try {
                        trayService.bandingProcess(trayData,operpData,count);
                    } catch (Exception e){
                        throw new RuntimeException(e.getMessage());
                    }
                    //如果是末序汇报，不管需不需要质检，直接生成入库单(末序需要校验只汇报一次)

                    //如果是末序汇报需要修改托盘的数量，原托- 所有料废数量
                    //需要判断末序是否完全汇报
                    Map<String,Object> trayMap = new HashMap<>();
                    trayMap.put("fbillno",trayNo);
                    trayMap.put("failCount",failCount + failQty);
                    trayService.updatetrayCount(trayMap);
                    prdInstockService.createPrdInstock(map, userName,user_id,REPORTkBillNo,failCount + + failQty);

                } else {
                    throw new RuntimeException("末序不允许分批汇报,需要满足末序的完工数量+之前的废料数量=托盘数量");
                }
            } else {//如果不是末序
                if (failCount + endQty == fqty){
                    double sumQuaQty = VtuzxUtil.getDouble(countMap,"sumQuaQty");//之前汇报的合格数量总和
                    double quaQty = VtuzxUtil.getDouble(map,"quaQty");//此次汇报的合格数量
                    double count = sumQuaQty + quaQty;
                    try {
                        trayService.bandingProcess(trayData,operpData,count);
                    } catch (Exception e){
                        throw new RuntimeException(e.getMessage());
                    }
                }
            }

        }


        //保存到数据库
        boolean savehead = sfcOptrptService.save(sfcOptrpt);
        boolean saveBatch =  sfcOptrptDetailService.saveBatch(sfcOptrptDetailList);


        return true;
    }

    /**
     * 查询之前汇报的完工数量总和,合格数量总和
     * @param operNo
     * @param trayNo
     * @return
     */
    public Map<String,Object> optrptCount(Integer operNo,String trayNo){
        List<SfcOptrpt> optrpts = sfcOptrptDao.selectList(new LambdaQueryWrapper<SfcOptrpt>()
                .eq(SfcOptrpt::getTrayNo, trayNo)
                .eq(SfcOptrpt::getOperpNo, operNo)
                .eq(SfcOptrpt::getWorkShopName, "机加车间"));
        List<String> billNoList = optrpts.stream()
                .map(SfcOptrpt::getBillNo)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        double sumQuaQty = 0.0;//合格数量总和
        double sumEndQty = 0.0;//完工数量总和

        if (!VtuzxUtil.isEmpty(billNoList)){
            List<SfcOptrptDetail> sfcOptrptDetails = sfcOptrptDetailDao.selectList(new LambdaQueryWrapper<SfcOptrptDetail>()
                    .in(SfcOptrptDetail::getBillNo, billNoList));
            for (SfcOptrptDetail sfcOptrptDetail : sfcOptrptDetails){
                Double quaQty = sfcOptrptDetail.getQuaQty();//合格数量
                Double endQty = sfcOptrptDetail.getEndQty();//完工数量
                sumEndQty += endQty;
                sumQuaQty += quaQty;
            }
        }

        Map<String,Object> data = new HashMap<>();
        data.put("sumQuaQty",sumQuaQty);
        data.put("sumEndQty",sumEndQty);
        return data;
    }

    /**
     * 查询已经汇报过的所有料废数量总和
     * @param operNo
     * @param trayNo
     * @return
     */
    public Double getFailCount(Integer operNo,String trayNo){
        List<SfcOptrpt> optrpts = sfcOptrptDao.selectList(new LambdaQueryWrapper<SfcOptrpt>()
                .eq(SfcOptrpt::getTrayNo, trayNo)
                .eq(SfcOptrpt::getWorkShopName, "机加车间"));
        List<String> billNoList = optrpts.stream()
                .map(SfcOptrpt::getBillNo)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        double failCount = 0.0;//废料数量总和

        if (!VtuzxUtil.isEmpty(billNoList)){
            List<SfcOptrptDetail> sfcOptrptDetails = sfcOptrptDetailDao.selectList(new LambdaQueryWrapper<SfcOptrptDetail>()
                    .in(SfcOptrptDetail::getBillNo, billNoList));
            failCount = sfcOptrptDetails.stream()
                    .mapToDouble(d -> d.getProcessFailQty() + d.getMaterialFailQty())
                    .sum();
        }

        return failCount;
    }

    /**
     * 根据托盘查询汇报详情
     * @param map
     * @param userName
     * @param user_id
     * @return
     * @throws Exception
     */
    @Override
    public Map<String, Object> getOptrptByTray(Map<String, Object> map, String userName, Long user_id) throws Exception {
        String trayNo = VtuzxUtil.getString(map,"billNo");
        List<SfcOptrpt> sfcOptrptList = sfcOptrptDao.selectList(new LambdaQueryWrapper<SfcOptrpt>()
                .eq(SfcOptrpt::getTrayNo, trayNo)
                .eq(SfcOptrpt::getWorkShopName, "机加车间"));
        if (!VtuzxUtil.isEmpty(sfcOptrptList)){
            List<String> billNoList = sfcOptrptList.stream()
                    .map(SfcOptrpt::getBillNo)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            //查询汇报明细
            List<SfcOptrptDetail> sfcOptrptDetails = sfcOptrptDetailDao.selectList(new LambdaQueryWrapper<SfcOptrptDetail>()
                    .in(SfcOptrptDetail::getBillNo, billNoList));
            Map<String,Object> data = new HashMap<>();
            data.put("dataList",sfcOptrptDetails);
            return data;

        } else {
            return null;
        }
    }

    /**
     * 修改工序汇报详情
     * @param map
     * @param userName
     * @param user_id
     * @return
     * @throws Exception
     */
    @Override
    public Boolean updateOprptDetail(Map<String, Object> map, String userName, Long user_id) throws Exception {
        //每次修改都要往日志表里存储,然后修改
        String billNo = VtuzxUtil.getString(map,"billNo");//工序汇报单号
        SfcOptrptDetail sfcOptrptDetail = sfcOptrptDetailDao.selectOne(new LambdaQueryWrapper<SfcOptrptDetail>()
                .eq(SfcOptrptDetail::getBillNo, billNo));
        List<Map<String,Object>> detailList =  VtuzxUtil.getObject(map,"detail");//工序汇报详情
        Map<String,Object> detail = detailList.get(0);
        String flot = VtuzxUtil.getString(detail, "flot");
        int beginandend = VtuzxUtil.getInt(detail,"beginandend");
        //如果是末序的话，判断是否生成入库单，如果生成了，判断入库单是否传金蝶，没传金蝶可以修改入库数量，传金蝶不允许修改
        //没生成入库单的话，生成入库单
        if (beginandend == 1){
            PrdInstock prdInstock = prdInstockDao.selectOne(new LambdaQueryWrapper<PrdInstock>()
                    .eq(PrdInstock::getREPORTNO, billNo));
            if (!VtuzxUtil.isEmpty(prdInstock)){//如果已经生成
                String fstate = prdInstock.getFSTATE();
                if ("audit".equals(fstate)){
                    throw new RuntimeException("入库单已经审核无法修改");
                } else {//修改入库单的数量和批次号
                    List<PrdInstockDetail> prdInstockDetailList = prdInstockDetailDao.selectList(new LambdaQueryWrapper<PrdInstockDetail>()
                            .eq(PrdInstockDetail::getFBILLNO, prdInstock.getFBillNo()));
                    for (PrdInstockDetail prdInstockDetail : prdInstockDetailList){
                        prdInstockDetail.setFMUSTQTY(VtuzxUtil.getDouble(detail,"quaQty"));
                        prdInstockDetail.setFREALQTY(VtuzxUtil.getDouble(detail,"quaQty"));

                        String stockFlot = prdInstockDetail.getFLOT();//入库的批号
                        if(!VtuzxUtil.isEmpty(flot) && !flot.equals(stockFlot)){
                            prdInstockDetail.setFLOT(flot);
                        }

                    }
                    prdInstockDetailService.saveOrUpdateBatch(prdInstockDetailList);
                    //修改托盘的数量
                    Map<String,Object> trayMap = new HashMap<>();
                    trayMap.put("fbillno",sfcOptrptDetail.getTrayNo());
                    trayMap.put("fqty",VtuzxUtil.getDouble(detail,"quaQty"));
                    trayMap.put("flot",flot);

                    trayService.updatetrayCountByPda(trayMap);
                }
            }

        }

        SfcOptrptLog sfcOptrptLog = new SfcOptrptLog();
        sfcOptrptLog.setOptId(sfcOptrptDetail.getId());
        sfcOptrptLog.setReportNo(billNo);
        sfcOptrptLog.setTrayNo(sfcOptrptDetail.getTrayNo());
        sfcOptrptLog.setQuaQty(sfcOptrptDetail.getQuaQty());
        sfcOptrptLog.setProcessFailQty(sfcOptrptDetail.getProcessFailQty());
        sfcOptrptLog.setMaterialFailQty(sfcOptrptDetail.getMaterialFailQty());
        sfcOptrptLog.setEndQty(sfcOptrptLog.getEndQty());
        sfcOptrptLog.setCreateUser(userName);
        sfcOptrptLog.setCreateTime(new Date());
        sfcOptrptLog.setOperpNo(sfcOptrptDetail.getOperpNo());
        sfcOptrptLog.setDescribtion(sfcOptrptDetail.getDescribtion());
        sfcOptrptLog.setFlot(flot);
        //日志保存修改前的数据
        sfcOptrptLogService.saveOrUpdate(sfcOptrptLog);

        //修改本次的数量信息
        sfcOptrptDetail.setQuaQty(VtuzxUtil.getDouble(detail,"quaQty"));
        sfcOptrptDetail.setProcessFailQty(VtuzxUtil.getDouble(detail,"processFailQty"));
        sfcOptrptDetail.setMaterialFailQty(VtuzxUtil.getDouble(detail,"materialFailQty"));
        sfcOptrptDetail.setEndQty(VtuzxUtil.getDouble(detail,"endQty"));
        sfcOptrptDetail.setFlot(flot);
        boolean b = sfcOptrptDetailService.updateById(sfcOptrptDetail);


        return b;
    }
}

